package org.codetranslators.compiler.mips.translate;

import java.util.Vector;

import org.codetranslators.common.Label;

public class ActivationFrame 
{
	public static final int WORDSIZE = 4;
	public static final int MIPS = 1;
	public static final int X86 = 2;
	
	public static final int NUM_CALLER_SAVED_REGISTERS = 10;
	public static final int NUM_CALLEE_SAVED_REGISTERS = 8;
	
	private Label name;
	private int currentOffSet;
	private int numEscapedVariables;
	private int maxOutgoingParams;
	private int numRegistersUsed;
	
	public static final String tab = "\t";
	public static final String newLine = "\r\n";
	
	public ActivationFrame(Label nm)
	{
		name = nm;
		currentOffSet = 0;
	}
	
	public Label getLabel()
	{
		return name;
	}
	
	public int getCurrentOffSet()
	{
		return currentOffSet;
	}
	
	public void setCurrentOffSet(int i)
	{
		currentOffSet = i;
	}
	
	public Vector getPrologueInstructions()
	{
		int stackSize = getStackSize();
	
		Vector prologueList = new Vector();
		prologueList.addElement(tab + "# FUNCTION PROLOGUE" + newLine);
		
		prologueList.addElement(tab + "subu $sp, $sp, " + stackSize + newLine);   // Set up the stack

		//Save the callee-saved registers
		int offSetFromSP =  1                    // The static link
						  + maxOutgoingParams    // The parameters
						  + 1;                   // The first callee-saved register ($s0) 
		int offSetInBytes = offSetFromSP * WORDSIZE;	
		int i;
		for(i = 0; i < numRegistersUsed; i++)
		{
			if(i > 7) break;
			
			String regToSave = "$s" + i;
			prologueList.addElement(tab + "sw " + regToSave + ", " +  offSetInBytes + "($sp)" + newLine);
			offSetFromSP++;
			offSetInBytes = offSetFromSP * WORDSIZE;
		}
		
		// Update the offset to cover for the remaining empty callee-saved registers area (if any)
		if(i <= 7)
			offSetFromSP += 7 - i;      

		// Save the return address
		offSetFromSP += 10;      // The caller-saved register region
		offSetInBytes = offSetFromSP * WORDSIZE;
		prologueList.addElement(tab + "sw $ra, " +  offSetInBytes + "($sp)" + newLine);
		
		// Save the frame pointer
		offSetFromSP++;      // The caller-saved register region
		offSetInBytes = offSetFromSP * WORDSIZE;
		prologueList.addElement(tab + "sw $fp, " +  offSetInBytes + "($sp)" + newLine);
		
		// Update the frame pointer
		prologueList.addElement(tab + "addu $fp, $sp, " + stackSize + newLine);
		
		prologueList.addElement(tab + "# END FUNCTION PROLOGUE" + newLine + newLine);
		return prologueList;
	}
	
	public Vector getEpilogueInstructions()
	{
		Vector epilogueList = new Vector();
		epilogueList.addElement(tab + "# FUNCTION EPILOGUE" + newLine);

		// Restore the frame pointer
		int offSetFromSP =  1                       // The static link
						   + maxOutgoingParams      // The parameters 
						   + 8                      // The callee-saved registers ($s0 - $s7)
						   + 10                     // The caller-saved registers ($t0 - $t9)
						   + 1;                     // The return address
		
		int offSetInBytes = offSetFromSP * WORDSIZE;
		epilogueList.addElement(tab + "lw $fp, " + offSetInBytes + "($sp)" + newLine);
		
		// Restore the return address
		offSetFromSP--;
		offSetInBytes = offSetFromSP * WORDSIZE;
		epilogueList.addElement(tab + "lw $ra, " + offSetInBytes + "($sp)" + newLine);
		
		offSetFromSP++;
		
		// Restore callee-saved registers
		offSetFromSP -= 10;   // All caller saved registers
		offSetFromSP -= 8;    // All callee-saved registers
		offSetInBytes = offSetFromSP * WORDSIZE;
		for(int i = 0; i < numRegistersUsed; i++)
		{			
			String regToSave = "$s" + i;
			epilogueList.addElement(tab + "lw " + regToSave + ", " +  offSetInBytes + "($sp)" + newLine);
			offSetFromSP++;
			offSetInBytes = offSetFromSP * WORDSIZE;
		}
		
		// Restore original stack pointer
		int stackSize = getStackSize();
		epilogueList.addElement(tab + "addu $sp, $sp, " + stackSize + newLine);
		
		// Return to the caller
		epilogueList.addElement(tab + "jr $ra " + newLine);
		
		epilogueList.addElement(tab + "# END FUNCTION EPILOGUE" + newLine + newLine);
		return epilogueList;
	}
	
	public static Vector getColors(int architecture)
	{
		Vector colors = new Vector();
		if(architecture == MIPS)
		{
			colors.addElement("$s0"); colors.addElement("$s1"); colors.addElement("$s2"); colors.addElement("$s3");
			colors.addElement("$s4"); colors.addElement("$s5"); colors.addElement("$s6"); colors.addElement("$s7");
			colors.addElement("$t0"); colors.addElement("$t1"); colors.addElement("$t2"); colors.addElement("$t3");
			colors.addElement("$t4"); colors.addElement("$t5"); colors.addElement("$t6"); colors.addElement("$t7");
			colors.addElement("$t8"); colors.addElement("$t9");
		}
		
		return colors;
	}
	
	public int getStackSize()
	{
		int stackElementNum =  numEscapedVariables      // Space for all the local variables to be on this stack
		 + 1                        // Space for the frame pointer of this stack
		 + 1                        // Space for the return address of this stack
		 + 10                       // Space for the caller-saved registers ($t0 - $t9)
		 + 8                        // Space for the callee-saved registers ($s0 - $s7)
		 + maxOutgoingParams        // Space for the outgoing parameters (maximum number)
		 + 1;                       // Space for the static link
		
		return stackElementNum * WORDSIZE;
		
	}
	
	public void setNumRegistersUsed(int numRegistersUsed)
	{
		this.numRegistersUsed = numRegistersUsed;
	}

	public void setMaxOutgoingParams(int maxOutgoingParams) {
		this.maxOutgoingParams = maxOutgoingParams;
	}
	
}
